﻿// (c)2010 David Schwarz (david.schwarz@live.com)
// This file is licensed under GNU GPL v2 license, see more at http://www.gnu.org/licenses/gpl-2.0.html
namespace Z2.Core
{

	#region Using

	using System;
	using System.Timers;
	using Microsoft.Xna.Framework;

	#endregion

	/// <summary>
	/// Třída, která definuje kameru.
	/// </summary>
	public class Camera
	{

		#region Members

		private static Camera _instance;

		private GraphicsDeviceManager m_device;

		private Vector2 m_position; // Současná pozice
		private Vector2 m_positionLast; // Předchozí pozice
		private ObjectBase m_follow; // Objekt, který je sledován

		private Rectangle m_screen;
		private Vector2 m_center;
		private Timer m_update;
		private bool m_locked;

		#endregion

		#region Constructor

		/// <summary>
		/// Konstruktor
		/// </summary>
		/// <param name="device"><see cref="GraphicsDeviceManager"/></param>
		public Camera(GraphicsDeviceManager device)
		{
			this.m_device = device;
			this.m_position = new Vector2();
			this.m_positionLast = new Vector2();
			this.m_follow = null;
			this.m_screen = new Rectangle(0, 0, device.PreferredBackBufferWidth, device.PreferredBackBufferHeight);
			this.m_center = new Vector2(this.m_screen.Width / 2, this.m_screen.Height / 2);

			this.m_update = new Timer();
			this.m_update.Interval = 10;
			this.m_update.Elapsed += new ElapsedEventHandler(Update);
			this.m_update.Enabled = true;

			_instance = this;
		}

		#endregion

		#region Properties

		/// <summary>
		/// Vrací instanci kamery
		/// </summary>
		public static Camera Instance
		{
			get
			{
				return _instance;
			}
		}

		/// <summary>
		/// Vrací rect, který definuje aktuální obrazovku
		/// </summary>
		public Rectangle Screen
		{
			get
			{
				return this.m_screen;
			}
		}

		/// <summary>
		/// Vrací pozici kamery
		/// </summary>
		public Vector2 Position
		{
			get
			{
				return this.m_position;
			}
		}

		/// <summary>
		/// Vrací nebo nastavuje objekt, který má kamera sledovat
		/// </summary>
		public ObjectBase Follow
		{
			get
			{
				return this.m_follow;
			}
			set
			{
				this.m_follow = value;
			}
		}

		#endregion

		#region Methods

		/// <summary>
		/// Metoda zamkne pohyb kamery
		/// </summary>
		public void Lock()
		{
			this.m_locked = true;
		}

		/// <summary>
		/// Metoda odemkne pohyb kamery
		/// </summary>
		public void Unlock()
		{
			this.m_locked = false;
		}

		/// <remarks>
		/// TODO: Rewrite this ugly shit
		/// </remarks>
		/// <summary>
		/// Výpočet pozice kamery.
		/// Tohle budu muset udělat trochu líp, pač se mi to fakt nelíbí :))
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void Update(object sender, ElapsedEventArgs e)
		{
			if (this.m_locked || this.m_follow == null)
			{
				return;
			}

			this.m_positionLast.X = this.m_position.X;
			this.m_positionLast.Y = this.m_position.Y;
			Vector2 position = new Vector2(
				this.m_follow.Position.X + (m_follow.Sprite.FrameWidth / 2),
				this.m_follow.Position.Y + (m_follow.Sprite.FrameHeight/ 2));
			this.m_position.X += ((-this.m_position.X - (position.X) + this.m_center.X) / 10.0f);
			this.m_position.Y += ((-this.m_position.Y - (position.Y) + this.m_center.Y) / 10.0f);

			// Ale fuj tohleto. Jednoho krásného dne to udělám hezčí. Fakt!
			if (-this.m_position.X < (World.Current.Bounds.X * WorldField.SIZE)) 
				this.m_position.X = -(World.Current.Bounds.X * WorldField.SIZE);
			if (-this.m_position.Y < (World.Current.Bounds.Y * WorldField.SIZE)) 
				this.m_position.Y = -(World.Current.Bounds.Y * WorldField.SIZE);
			if (-this.m_position.X + this.m_device.PreferredBackBufferWidth > (World.Current.Bounds.Width * WorldField.SIZE))
				this.m_position.X = -((World.Current.Bounds.Width * WorldField.SIZE) - this.m_device.PreferredBackBufferWidth);
			if (-this.m_position.Y + this.m_device.PreferredBackBufferHeight > (World.Current.Bounds.Height * WorldField.SIZE))
				this.m_position.Y = -((World.Current.Bounds.Height * WorldField.SIZE) - this.m_device.PreferredBackBufferHeight);

			int screenX = -Convert.ToInt32(this.m_position.X ) / WorldField.SIZE;
			int screenY = -Convert.ToInt32(this.m_position.Y ) / WorldField.SIZE;
			int screenWidth = this.m_screen.X + (this.m_device.PreferredBackBufferWidth / WorldField.SIZE);
			int screenHeight = this.m_screen.Y + (this.m_device.PreferredBackBufferHeight / WorldField.SIZE);

			++screenWidth;
			++screenHeight;
			if (screenX < 0) screenX = World.Current.Bounds.X;
			if (screenY < 0) screenY = World.Current.Bounds.Y;
			if (screenWidth > World.Current.Bounds.Width) screenWidth = World.Current.Bounds.Width;
			if (screenHeight > World.Current.Bounds.Height) screenHeight = World.Current.Bounds.Height;

			this.m_screen.X = screenX;
			this.m_screen.Y =  screenY;
			this.m_screen.Width = screenWidth + 1;
			this.m_screen.Height = screenHeight + 1;
		}

		#endregion

	}
}
